package com.yhy.wxzn.wcs.device.rgv.core.plc;

import com.yhy.wxzn.common.utils.WcsUtils;
import com.yhy.wxzn.wcs.device.netty.bean.ResultData;
import com.yhy.wxzn.wcs.device.plc.bean.FourCarPlc;
import com.yhy.wxzn.wcs.device.plc.xj.XjPlcDriver;
import com.yhy.wxzn.wcs.device.rgv.bean.FourWayRgv;
import com.yhy.wxzn.wcs.device.rgv.bean.Fragment;
import com.yhy.wxzn.wcs.device.rgv.bean.Node;
import com.yhy.wxzn.wcs.device.rgv.enums.Action;
import com.yhy.wxzn.wcs.device.rgv.enums.PalletStatus;
import com.yhy.wxzn.wcs.device.rgv.enums.ReversingDirection;
import com.yhy.wxzn.wcs.device.rgv.enums.RgvProgress;
import com.yhy.wxzn.wcs.entity.WcsTask;
import com.yhy.wxzn.wcs.enums.TaskType;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static com.yhy.wxzn.wcs.SystemInit.Local_Test;

@Slf4j
public class ToRgv {

    private static final Map<FourWayRgv,ToRgv> toRgvMap  = new ConcurrentHashMap<>();
    private FourWayRgv rgv;
    private XjPlcDriver xjPlcDriver;
    private FourCarPlc fourCarPlc;

    private ToRgv(FourWayRgv rgv){
        this.rgv = rgv;
        this.xjPlcDriver = rgv.getXjPlcDriver();
        this.fourCarPlc = rgv.getFourCarPlc();
    }

    public static ToRgv getInstance(FourWayRgv fourWayRgv){
        ToRgv toRgv = toRgvMap.get(fourWayRgv);
        if (toRgv == null){
            toRgv = new ToRgv(fourWayRgv);
            toRgvMap.put(fourWayRgv,toRgv);
        }
        return toRgv;
    }


    public void stopCharge(){
        ResultData resultData = xjPlcDriver.write( "HD6", new byte[]{0x00, 0x02});
        if (resultData.isSuccess()) {
            log.info("{}下发停止充电命令成功", rgv.getDeviceInfo().getDeviceName() );
        }else {
            log.info( "{}下发停止充电命令失败,原因为{}", rgv.getDeviceInfo().getDeviceName(),resultData.getMessage());
        }
    }



    public boolean sendRunCommand( Fragment fragment, int serailNo){
        if (Local_Test) return true;
        List<Node> nodes = fragment.getNodes();
        boolean result = Boolean.FALSE;
        String writeAddress = fourCarPlc.getWriteAddress();
        ResultData resultData;

        resultData = writeCode(nodes);
        if(resultData.isSuccess()){
            resultData = writeXAxios(nodes);

            if(resultData.isSuccess()){

                resultData = writeYAxios(nodes);

                if(resultData.isSuccess()){
                    resultData = writeDrivingData(fragment,serailNo,writeAddress);
                    result = resultData.isSuccess() ? true : false;
                }
            }
        }

        return result;
    }

    private ResultData writeDrivingData(Fragment fragment,int serailNo,String writeAddress) {
        byte[] writeBytes = new byte[16];
        //行驶里程
        writeBytes[0] = (byte) ((fragment.getRoadHaul() >> 8) & 0xFF);
        writeBytes[1] = (byte) (fragment.getRoadHaul() & 0xFF);
        //速度
        writeBytes[2] = (byte) ((fragment.getSpeed() >> 8) & 0xFF);
        writeBytes[3] = (byte) ((fragment.getSpeed()) & 0xFF);
        //运行方向
        writeBytes[4] = (byte) ((fragment.getDirect().getCmd() >> 8) & 0xFF);
        writeBytes[5] = (byte) ((fragment.getDirect().getCmd()) & 0xFF);
        //启动
        writeBytes[6] = (byte) ((1 >> 8) & 0xFF);
        writeBytes[7] = (byte) ((1) & 0xFF);
        //升降
        //todo 需要判断不同发命令的情况
        writeBytes[8] = (byte) ((0 >> 8) & 0xFF);
        writeBytes[9] = (byte) ((0) & 0xFF);
        //心跳
        writeBytes[10] = (byte) ((1 >> 8) & 0xFF);
        writeBytes[11] = (byte) ((1) & 0xFF);
        //充电命令
        writeBytes[12] = (byte) ((0 >> 8) & 0xFF);
        writeBytes[13] = (byte) ((0) & 0xFF);
        //任务号
        writeBytes[14] = (byte) ((serailNo >> 8) & 0xFF);
        writeBytes[15] = (byte) (serailNo & 0xFF);
        return xjPlcDriver.write(writeAddress,writeBytes);
    }

    private ResultData writeXAxios(List<Node> nodes) {
        byte[] xbytes = new byte[4 * nodes.size()];

        for (int i = 0; i < nodes.size(); i++) {
            Node node = nodes.get(i);
            int offsetX = node.getOffsetX();

            xbytes[4 * i] = (byte) ((offsetX >> 8) & 0xFF);
            xbytes[4 * i + 1] = (byte) ((offsetX) & 0xFF);
            xbytes[4 * i + 2] = (byte) ((offsetX >> 24) & 0xFF);
            xbytes[4 * i + 3] = (byte) (offsetX >> 16 & 0xFF);
        }


        return xjPlcDriver.write("HD2000",xbytes);
    }


    private ResultData writeYAxios(List<Node> nodes) {
        byte[] xbytes = new byte[4 * nodes.size()];

        for (int i = 0; i < nodes.size(); i++) {
            Node node = nodes.get(i);
            int offsetY = node.getOffsetY();

            xbytes[4 * i] = (byte) ((offsetY >> 8) & 0xFF);
            xbytes[4 * i + 1] = (byte) ((offsetY ) & 0xFF);
            xbytes[4 * i + 2] = (byte) ((offsetY >> 24) & 0xFF);
            xbytes[4 * i + 3] = (byte) (offsetY >> 16& 0xFF);
        }


        return xjPlcDriver.write("HD3000",xbytes);
    }

    private ResultData writeCode(List<Node> nodes) {
        byte [] qrCode = new byte[4 * nodes.size()];

        for (int i = 0; i < nodes.size(); i++) {
            Integer groundCode = Integer.parseInt(nodes.get(i).getGroundCode());
            qrCode[4 * i] = (byte) ((groundCode >> 8) & 0xFF);
            qrCode[4* i + 1] = (byte) (groundCode & 0xFF);
            qrCode[4* i + 2] = (byte) ((groundCode >> 24) & 0xFF);
            qrCode[4* i + 3] = (byte) ((groundCode >> 16) & 0xFF);
        }



        //写入二维码
        return xjPlcDriver.write("HD4000",qrCode);
    }


    public void waitRunCommandFinish(int toRgvTaskNo, long sendTime) {
        if (Local_Test) return;
        int time = 0;
       while (true){
           rgv.checkIntercept("waitRunCommandFinish");
           FourCarPlc fourCarPlc = ResultContainer.getResultContainer(this.rgv).getTaskResult(toRgvTaskNo);
           if (fourCarPlc != null && fourCarPlc.getBeFinish() == 1 && sendTime < fourCarPlc.getReceiveTime())break;
           if (time++ % 100 == 0) log.info("{} taskId:{} toRgvTaskNo:{} 等待任务执行结束",rgv.toString(),rgv.getCurTask().getTaskId(),toRgvTaskNo);
           WcsUtils.sleep(100);
       }
    }


    public void palletUp() {
        if (fourCarPlc.getPosition() != Action.下降或副通道.getCmd()){
            log.info("{}小车换向状态不为下降，托盘无法顶升",rgv.getName());
            return;
        }


        //todo 确定写入地址
        ResultData resultData = xjPlcDriver.write( "HD4", new byte[]{0x00, 0x01});
        if (resultData.isSuccess()) {
            log.info("{}下发托盘顶升命令成功", rgv.getName() );
        }else {
            log.error( "{}下发托盘顶升命令失败,原因为{}", rgv.getName(),resultData.getMessage());
            throw new RuntimeException(String.format("{}下发托盘顶升命令失败,原因为{}", rgv.getName(),resultData.getMessage()));
        }

        while (fourCarPlc.getPosition() != 1){
            log.info("{}等待托盘顶升完成",rgv.getName());
            WcsUtils.sleep(100);
            rgv.checkIntercept(String.format("{}等待托盘顶升完成",rgv.getName()));
        }

    }


    public void palletDown() {
        if (fourCarPlc.getPosition() != Action.顶升.getCmd()){
            log.info("{}小车换向状态不为顶升，托盘无法下降",rgv.getName());
            return;
        }


        //todo 确定写入地址
        ResultData resultData = xjPlcDriver.write( "HD4", new byte[]{0x00, 0x02});
        if (resultData.isSuccess()) {
            log.info("{}下发托盘下降命令成功", rgv.getName());
        }else {
            log.info( "{}下发托盘下降命令失败,原因为{}", rgv.getName(),resultData.getMessage());
            throw new RuntimeException(String.format("{}下发托盘下降命令失败,原因为{}", rgv.getName(),resultData.getMessage()));
        }

        while (fourCarPlc.getPosition() != 2){
            log.info("{}等待托盘下降完成",rgv.getName());
            WcsUtils.sleep(100);
            rgv.checkIntercept(String.format("{}等待托盘下降完成",rgv.getName()));
        }
    }


    public void checkDirectAndCommutate(WcsTask task,ReversingDirection reversingDirection) {
        reversingDirection = ensureReversingDirection(task,reversingDirection);
        checkDirectAndCommutate(reversingDirection);
    }
    public void checkDirectAndCommutate(ReversingDirection reversingDirection) {

        ReversingDirection curReversingDirection = fourCarPlc.getReversingDirection();

        if (reversingDirection == curReversingDirection) return;


        ResultData resultData = xjPlcDriver.write( "HD4", reversingDirection.getCmd());
        if (resultData.isSuccess()) {
            log.info("{}下发换向命令 {} 成功", rgv.getName(),reversingDirection);
        }else {
            log.info( "{}下发换向命令 {} 失败,原因为{}", rgv.getName(),reversingDirection,resultData.getMessage());
            throw new RuntimeException(String.format("%s下发换向命令%s失败,原因为%s", rgv.getName(),reversingDirection,resultData.getMessage()));
        }


        while (true){
             curReversingDirection = fourCarPlc.getReversingDirection();
            if (curReversingDirection == reversingDirection) return;
            WcsUtils.sleep(100);
            rgv.checkIntercept("checkDirectAndCommutate");
        }

    }

    private ReversingDirection ensureReversingDirection(WcsTask task, ReversingDirection reversingDirection) {
        if (reversingDirection == ReversingDirection.副通道 && task.getRgvProgress() == RgvProgress.RGV_TO_END &&(
                task.getTaskType() == TaskType.IN
                || task.getTaskType() == TaskType.OUT
                || task.getTaskType() == TaskType.MOVE_LOADED
                || task.getTaskType() == TaskType.TRANSFER
                ))
            return ReversingDirection.顶升副通道;
        return reversingDirection;
    }

    /**
     * 清除报警信息
     */
    public void clearErrorMsg(){
        xjPlcDriver.write("HD8",1);
    }
}
